﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using xycn.framework.PluginManagement;
using xycn.pluginsupport.ControllerPlugins;

namespace xycn.framework.ControllerManagement
{
    /// <summary>
    /// WebAPI controller加载器
    /// </summary>
    public class ControllerLoader
    {
        /// <summary>
        /// controller库加载错误信息
        /// </summary>
        public static string errMsg = "";

        /// <summary>
        /// 加载controller插件
        /// </summary>
        /// <param name="ret_controller">返回加载的controller列表</param>
        /// <returns></returns>
        public static int LoadControllers(string controller_path, out List<ControllerInvoker> invokers)
        {
            int ret = 0;

            List<ControllerInfor> load_controllers = new List<ControllerInfor>();
            invokers = null;
            
            Assembly controller_assembly = null;
            try
            {
                controller_assembly = GlobalContext.LoadPlugin(controller_path);
                
                /*if (!haveControllerRef(controller_assembly))
                {
                    errMsg = "分析程序集：" + controller_assembly.FullName +
                            "时，出现错误。该程序集不是正确的Controller插件程序...";
                    return -1;
                }*/

                try
                {
                    //获取dll中所有的类
                    Type[] types = controller_assembly.GetTypes();
                    foreach (Type type in types)
                    {
                        //判断类是否包含ApiController属性，从而确定是否包含controller类
                        object[] attributes =
                            type.GetCustomAttributes(typeof(ApiController), true);

                        if (attributes.Count() <= 0) continue;

                        //缓存controller信息
                        ControllerInfor controller = new ControllerInfor();
                        controller.className = type.Name;

                        Route[] routes =
                            (Route[])type.GetCustomAttributes(typeof(Route), true);
                        //优先根据Route属性设置controller路由
                        if (routes.Count() > 0)
                        {
                            foreach (Route route in routes)
                            {
                                Route new_route = new Route(route.mapRoute);
                                new_route.mapRoute =
                                    route.mapRoute.Replace("[controller]", type.Name);
                                //controller作为绝对路径
                                if (new_route.mapRoute[0] != '/')
                                {
                                    new_route.mapRoute = "/" + new_route.mapRoute;
                                }
                                new_route.routeOrder = route.routeOrder;
                                controller.controllerRoutes.Add(new_route);
                            }
                        }
                        else
                        {
                            controller.controllerRoutes.Add(new Route("/" + type.Name));
                        }

                        controller.controllerType = type;
                        controller.fromLib = controller_path;
                        controller.Namespace = type.Namespace;

                        AnalyzeControllerMethod(controller);

                        load_controllers.Add(controller);
                    }
                }
                catch (Exception ex)
                {
                    errMsg = "分析程序集：" + controller_assembly.FullName +
                        "时，出现错误," + ex.Message;
                    return -1;
                }
            }
            catch (Exception ex)
            {
                errMsg = errMsg + "[ERROR]：加载\"" + controller_path +
                    "\"时，出现错误，请确认是否为合法程序集...\r\n" + ex.Message;
                return -1;
            }

            
            CreateInvoker(load_controllers, out invokers);

            return ret;
        }

        /// <summary>
        /// 判断dll是否包含xycn.controller依赖库
        /// </summary>
        /// <param name="assembly"></param>
        /// <returns></returns>
        /*private static bool haveControllerRef(Assembly controller_assembly)
        {
            AssemblyName[] ref_names =
                            controller_assembly.GetReferencedAssemblies();

            //查找dll是否依赖于“xycn.controller”
            //以便根据依赖库名称判断是否为controller
            bool ret = false;
            foreach (AssemblyName ref_name in ref_names)
            {
                if (ref_name.Name == "xycn.controllersupport")
                {
                    ret = true;
                    break;
                }
            }

            return ret;
        }*/

        /// <summary>
        /// 分析控制器
        /// </summary>
        /// <param name="type"></param>
        /// <param name="controller_list"></param>
        private static void AnalyzeControllerMethod(ControllerInfor controller)
        {
            //创建controller中请求处理函数列表
            controller.methodInfors = new List<ControllerMethodInfor>();
            foreach (MethodInfo method in controller.controllerType.GetMethods())
            {
                ControllerMethodInfor controllermethod_info = null;

                //判断是否存在HttpMethodWithContext，从而确定是否为HTTP请求处理函数
                HttpMethodWithContext[] hm_wc_attrs =
                    (HttpMethodWithContext[])method.GetCustomAttributes(
                        typeof(HttpMethodWithContext), true);
                if (hm_wc_attrs.Count() > 0)
                {
                    HttpMethodWithContext httpmethod_attr = (HttpMethodWithContext)hm_wc_attrs[0];
                    controllermethod_info = new ControllerMethodInfor();
                    controllermethod_info.methodAttrType = MethodAttribute.HttpMethodWithContext;
                    controllermethod_info.methodAttr = hm_wc_attrs[0];
                    controllermethod_info.httpMethodType = httpmethod_attr.methodType;

                }

                //判断是否存在HttpMethod，从而确定是否为HTTP请求处理函数
                HttpMethod[] hm_attrs =
                    (HttpMethod[])method.GetCustomAttributes(typeof(HttpMethod), true);
                if (hm_attrs.Count() > 0)
                {
                    HttpMethod httpmethod_attr = (HttpMethod)hm_attrs[0];
                    controllermethod_info = new ControllerMethodInfor(); ;
                    controllermethod_info.methodAttrType = MethodAttribute.HttpMethod;
                    controllermethod_info.methodAttr = hm_attrs[0];
                    controllermethod_info.httpMethodType = httpmethod_attr.methodType;
                }

                //判断是否存在Action标记，从而确定是否为HTTP请求处理函数
                ActionMethod[] action_attrs =
                    (ActionMethod[])method.GetCustomAttributes(
                        typeof(ActionMethod), true);
                if (action_attrs.Count() > 0)
                {
                    ActionMethod action_attr =
                        (ActionMethod)action_attrs[0];
                    controllermethod_info = new ControllerMethodInfor();
                    controllermethod_info.methodAttrType = MethodAttribute.ActionMethod;
                    controllermethod_info.methodAttr = action_attrs[0];
                    controllermethod_info.httpMethodType = HttpMethodType.POST;
                }

                //判断是否存在ActionWithContext标记，从而确定是否为HTTP请求处理函数
                ActionWithContext[] action_wc_attrs =
                    (ActionWithContext[])method.GetCustomAttributes(
                        typeof(ActionWithContext), true);
                if (action_wc_attrs.Count() > 0)
                {
                    ActionWithContext action_attr =
                        (ActionWithContext)action_wc_attrs[0];
                    controllermethod_info = new ControllerMethodInfor();
                    controllermethod_info.methodAttrType = MethodAttribute.ActionWithContext;
                    controllermethod_info.methodAttr = action_wc_attrs[0];
                    controllermethod_info.httpMethodType = HttpMethodType.POST;
                }

                //自动创建默认的http处理方法
                if (controllermethod_info == null && (method.Name.ToLower() == "get" || method.Name.ToLower() == "put" ||
                        method.Name.ToLower() == "post" || method.Name.ToLower() == "delete"))
                {
                    HttpMethod hm_defaultattr = null;
                    if (method.Name.ToLower() == "get")
                        hm_defaultattr = new HttpMethod(HttpMethodType.GET);
                    if (method.Name.ToLower() == "put")
                        hm_defaultattr = new HttpMethod(HttpMethodType.PUT);
                    if (method.Name.ToLower() == "post")
                        hm_defaultattr = new HttpMethod(HttpMethodType.POST);
                    if (method.Name.ToLower() == "delete")
                        hm_defaultattr = new HttpMethod(HttpMethodType.DELETE);

                    controllermethod_info = new ControllerMethodInfor();
                    controllermethod_info.methodAttrType = MethodAttribute.HttpMethod;
                    controllermethod_info.methodAttr = hm_defaultattr;
                    controllermethod_info.httpMethodType = hm_defaultattr.methodType;
                }

                //如果未标记为http method，且请求方法，不是get、put、post、
                //delete则不作为Http请求处理函数处理
                if (controllermethod_info == null)
                {
                    continue;
                }

                #region 添加权限判断
                var auth_attrs = method.GetCustomAttributes(
                      typeof(AllowAnonymousAttribute), true);
                if (auth_attrs.Length > 0)
                {
                    controllermethod_info.IsAuth = false;
                }
                #endregion

                controllermethod_info.methodInfor = method;
                controllermethod_info.Name = method.Name;
                controllermethod_info.returnType = method.ReturnType;

                //创建控制器路由
                BuildRouter(controller, method, controllermethod_info);

                //分析函数输入参数
                AnalyzeMethodParmeter(method, controllermethod_info);
                controller.methodInfors.Add(controllermethod_info);

            }
        }

        /// <summary>
        /// 根据方法信息，创建方法对应的路由，路由允许叠加，路由生成的步骤如下：
        /// 1.根据方法自身属性，如[HttpMethod(GET,"XXXXXXX")]中XXXXX路由设置方法对应路由，
        /// 2.叠加Route属性中设置的路由
        /// 3.如果方法没有任何路由，则默认采用Controller的路由
        /// 4.如果方法有路由，还需要判断是否为绝对路径，否则将controller中指定路由叠加上去
        /// </summary>
        private static void BuildRouter(ControllerInfor controller, MethodInfo method,
            ControllerMethodInfor controllermethod_info)
        {
            List<Route> tp_routes = new List<Route>();

            //方法自身属性，带路由
            switch (controllermethod_info.methodAttrType)
            {
                case MethodAttribute.HttpMethodWithContext:
                    {
                        string route_str =
                            ((HttpMethodWithContext)controllermethod_info.methodAttr).mapRoute;
                        if (route_str != "")
                        {
                            int order = ((HttpMethodWithContext)controllermethod_info.methodAttr).routeOrder;

                            Route new_route = new Route("");
                            new_route.mapRoute =
                                route_str.Replace("[controller]", controller.className);
                            new_route.routeOrder = order;
                            tp_routes.Add(new_route);
                        }
                        break;
                    }
                case MethodAttribute.HttpMethod:
                    {
                        string route_str =
                                ((HttpMethod)controllermethod_info.methodAttr).mapRoute;
                        if (route_str != "")
                        {
                            int order = ((HttpMethod)controllermethod_info.methodAttr).routeOrder;

                            Route new_route = new Route("");
                            new_route.mapRoute =
                                route_str.Replace("[controller]", controller.className);
                            new_route.routeOrder = order;
                            tp_routes.Add(new_route);
                        }
                        break;
                    }
                case MethodAttribute.ActionWithContext:
                    {
                        string route_str =
                            ((ActionWithContext)controllermethod_info.methodAttr).mapRoute;
                        if (route_str != "")
                        {
                            //根据属性设置路由
                            int order = ((ActionWithContext)controllermethod_info.methodAttr).routeOrder;

                            Route new_route = new Route("");
                            new_route.mapRoute =
                                route_str.Replace("[controller]", controller.className);
                            new_route.routeOrder = order;
                            tp_routes.Add(new_route);
                        }
                        else
                        {
                            //Action将函数名作为默认路由
                            int order = ((ActionWithContext)controllermethod_info.methodAttr).routeOrder;
                            Route new_route = new Route("");
                            new_route.mapRoute = controllermethod_info.Name;
                            new_route.routeOrder = order;
                            tp_routes.Add(new_route);
                        }
                        break;
                    }
                case MethodAttribute.ActionMethod:
                    {
                        string route_str =
                                ((ActionMethod)controllermethod_info.methodAttr).mapRoute;
                        if (route_str != "")
                        {
                            //根据属性设置路由
                            int order = ((ActionMethod)controllermethod_info.methodAttr).routeOrder;

                            Route new_route = new Route("");
                            new_route.mapRoute =
                                route_str.Replace("[controller]", controller.className);
                            new_route.routeOrder = order;
                            tp_routes.Add(new_route);
                        }
                        else
                        {
                            //Action将函数名作为默认路由
                            int order = ((ActionMethod)controllermethod_info.methodAttr).routeOrder;
                            Route new_route = new Route("");
                            new_route.mapRoute = controllermethod_info.Name;
                            new_route.routeOrder = order;
                            tp_routes.Add(new_route);
                        }
                        break;
                    }
            }

            //根据路由标记[Route]设置路由
            Route[] routes = (Route[])method.GetCustomAttributes(typeof(Route), true);
            foreach (Route route in routes)
            {
                Route new_route = new Route("");
                new_route.mapRoute =
                    route.mapRoute.Replace("[controller]", controller.className);
                new_route.routeOrder = route.routeOrder;

                tp_routes.Add(new_route);
            }

            //方法啥路由都没有
            controllermethod_info.routeAttr = new List<Route>();
            if (tp_routes.Count() <= 0)
            {
                foreach (Route controller_route in controller.controllerRoutes)
                {
                    Route new_route = new Route("");
                    new_route.mapRoute =
                        controller_route.mapRoute;
                    new_route.routeOrder = 0;
                    controllermethod_info.routeAttr.Add(new_route);
                }
            }
            else
            {
                //叠加controller所具备的路由
                foreach (Route route in tp_routes)
                {
                    //表明使用相对路径
                    if (route.mapRoute[0] != '/')
                    {
                        foreach (Route controller_route in controller.controllerRoutes)
                        {
                            Route new_route = new Route("");
                            new_route.mapRoute =
                                controller_route.mapRoute + "/" + route.mapRoute;
                            new_route.routeOrder = route.routeOrder;
                            controllermethod_info.routeAttr.Add(new_route);
                        }
                    }
                    else
                    {
                        controllermethod_info.routeAttr.Add(route);
                    }
                }
            }

            tp_routes.Clear();

            //统一去除可能误加的"//"
            foreach (Route route in controllermethod_info.routeAttr)
            {
                route.mapRoute = route.mapRoute.Replace("//", "/");
            }
        }

        /// <summary>
        /// 分析HTTP请求处理函数输入参数信息
        /// </summary>
        /// <param name="method"></param>
        /// <param name="controllermethod_info"></param>
        private static void AnalyzeMethodParmeter(MethodInfo method, ControllerMethodInfor controllermethod_info)
        {
            //创建controller中请求处理函数列表
            controllermethod_info.Parameters = new List<MethodParmeter>();
            foreach (ParameterInfo parmeter in method.GetParameters())
            {
                MethodParmeter method_parmeter = new MethodParmeter();

                method_parmeter.parmeterType = parmeter.ParameterType;
                method_parmeter.Name = parmeter.Name;
                if (parmeter.GetCustomAttributes(typeof(FromQuery), true).Count() > 0)
                    method_parmeter.Attribute = "FromQuery";
                if (parmeter.GetCustomAttributes(typeof(FromForm), true).Count() > 0)
                    method_parmeter.Attribute = "FromForm";
                if (parmeter.GetCustomAttributes(typeof(FromBody), true).Count() > 0)
                    method_parmeter.Attribute = "FromBody";
                if (parmeter.GetCustomAttributes(typeof(FromHeader), true).Count() > 0)
                    method_parmeter.Attribute = "FromHeader";
                if (parmeter.GetCustomAttributes(typeof(FromRoute), true).Count() > 0)
                    method_parmeter.Attribute = "FromRoute";

                controllermethod_info.Parameters.Add(method_parmeter);
            }
        }

        /// <summary>
        /// 创建控制器插件调用接口
        /// </summary>
        /// <param name="controllers"></param>
        /// <returns></returns>
        public static void CreateInvoker(List<ControllerInfor> controllers,
            out List<ControllerInvoker> invokers)
        {
            invokers = new List<ControllerInvoker>();

            invokers = new List<ControllerInvoker>();

            foreach (ControllerInfor controller in controllers)
            {
                foreach (ControllerMethodInfor method in controller.methodInfors)
                {

                    ControllerInvoker invoker = new ControllerInvoker();
                    invoker.fromLib = controller.fromLib;
                    invoker.Namespace = controller.Namespace;
                    invoker.className = controller.className;
                    invoker.controllerType = controller.controllerType;
                    invoker.controllerBaseType = controller.controllerType.BaseType;
                    invoker.methodInfor = method.methodInfor;
                    invoker.methodName = method.Name;
                    invoker.IsAuth = method.IsAuth;
                    invoker.httpMethodType = method.httpMethodType;
                    invoker.methodAttrType = method.methodAttrType;

                    //tobemodified 
                    //考虑建立controller缓存，为每个controller对象，创建委托以提高HttpContext的效率
                    if (invoker.methodAttrType == MethodAttribute.HttpMethodWithContext ||
                        invoker.methodAttrType == MethodAttribute.ActionWithContext)
                    {
                        int x = 0;
                        //invoker.httpMethodWithContextFunc = 
                        //    method.methodInfor.CreateDelegate<Func<HttpContext, Task>>();  
                    }
                    //tobemodified over

                    invoker.httpMethodAttr = method.methodAttr;
                    invoker.routeAttrs.AddRange(method.routeAttr);
                    invoker.Parameters.AddRange(method.Parameters);
                    invoker.returnType = method.returnType;
                    invokers.Add(invoker);
                }
            }
        }
    }
}
